/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package skyproc;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.DataFormatException;
import lev.LImport;
import skyproc.exceptions.BadParameter;
import skyproc.exceptions.BadRecord;

/**
 *
 * @author Justin Swanson
 */
abstract class SubRecords implements Serializable, Iterable<SubRecord> {

    protected Map<String, SubRecord> map = new HashMap<>(0);

    public SubRecords() {
    }

    public void setMajor(MajorRecord in) {
    }

    public void setPrototype(SubPrototype proto) {
    }

    public abstract SubPrototype getPrototype();

    public void add(SubRecord r) {
        for (String t : r.getTypes()) {
            map.put(t, r);
        }
    }

    protected void export(ModExporter out) throws IOException {
        for (SubRecord s : this) {
            s.export(out);

        }
    }

    void clear() {
        map.clear();
    }

    public boolean shouldExport(SubRecord s) {
        return s.isValid();
    }

    public boolean contains(String t) {
        return map.containsKey(t);
    }

    public SubRecord get(String in) {
        return map.get(in);
    }

    public SubString getSubString(String in) {
        return (SubString) get(in);
    }

    public void setSubString(String in, String str) {
        getSubString(in).setString(str);
    }

    public SubStringPointer getSubStringPointer(String in) {
        return (SubStringPointer) get(in);
    }

    public void setSubStringPointer(String in, String s) {
        getSubStringPointer(in).setText(s);
    }

    public SubForm getSubForm(String in) {
        return (SubForm) get(in);
    }

    public void setSubForm(String in, FormID id) {
        getSubForm(in).setForm(id);
    }

    public SubFloat getSubFloat(String in) {
        return (SubFloat) get(in);
    }

    public void setSubFloat(String in, float f) {
        getSubFloat(in).set(f);
    }

    public SubData getSubData(String in) {
        return (SubData) get(in);
    }

    public void setSubData(String in, byte[] b) {
        getSubData(in).setData(b);
    }

    public void setSubData(String in, int i) {
        getSubData(in).setData(i);
    }

    public void setSubData(String in, int i, int size) {
        getSubData(in).setData(i, size);
    }

    public SubFlag getSubFlag(String in) {
        return (SubFlag) get(in);
    }

    public void setSubFlag(String in, int i, boolean b) {
        getSubFlag(in).set(i, b);
    }

    public SubInt getSubInt(String in) {
        return (SubInt) get(in);
    }

    public void setSubInt(String in, int i) {
        getSubInt(in).set(i);
    }

    public SubRGB getSubRGB(String in) {
        return (SubRGB) get(in);
    }

    public void setSubRGB(String in, RGB c, float f) {
        getSubRGB(in).set(c, f);
    }

    public SubRGBshort getSubRGBshort(String in) {
        return (SubRGBshort) get(in);
    }

    public void setSubRGBshort(String in, RGBA c, short val) {
        getSubRGBshort(in).set(c, val);
    }

    public SubMarkerSet getSubMarker(String in) {
        return (SubMarkerSet) get(in);
    }

    public KeywordSet getKeywords() {
        return (KeywordSet) get("KWDA");
    }

    public SubFormArray getSubFormArray(String in) {
        return (SubFormArray) get(in);
    }

    public SubList getSubList(String in) {
        return (SubList) get(in);
    }

    public ScriptPackage getScripts() {
        return (ScriptPackage) get("VMAD");
    }

    public BodyTemplate getBodyTemplate() {
        return (BodyTemplate) get("BODT");
    }

    public SubShell getSubShell(String t) {
        return (SubShell) get(t);
    }

    public SubFormInt getSubFormInt(String t) {
        return (SubFormInt) get(t);
    }

    public void setSubFormInt(String t, FormID id, int val) {
        SubFormInt s = getSubFormInt(t);
        s.setForm(id);
        s.setNum(val);
    }

    public void setSubFormInt(String t, FormID id) {
        getSubFormInt(t).setForm(id);
    }

    public void setSubFormInt(String t, int val) {
        getSubFormInt(t).setNum(val);
    }

    public Model getModel() {
        return (Model) get("MODL");
    }

    boolean isValid() {
        for (SubRecord s : this) {
            if (!s.isValid()) {
                return false;
            }
        }
        return true;
    }

    boolean isAnyValid() {
        for (SubRecord s : this) {
            if (s.isValid()) {
                return true;
            }
        }
        return false;
    }

    ArrayList<String> summaryLines() {
        ArrayList<String> out = new ArrayList<>();
        String header = "Summary: ";
        String data = "";
        int counter = 0;
        ArrayList<String> printedTypes = new ArrayList<>();
        for (String type : getTypes()) {
            SubRecord s = map.get(type);
            if (s != null && s.isValid() && !printedTypes.contains(type)) {
                data = data + type + " ";
                if (s instanceof SubList) {
                    data = data + "(" + ((SubList) s).size() + ") ";
                }
                printedTypes.addAll(s.getTypes());
                if (counter++ == 12) {
                    out.add(header + data);
                    header = "-------- ";
                    data = "";
                    counter = 0;
                }
            }
        }
        if (counter > 0) {
            out.add(header + data);
        }
        return out;
    }

    void importSubRecords(LImport in, Mod srcMod) throws BadRecord, BadParameter, DataFormatException {
        while (!in.isDone()) {
            importSubRecord(in, srcMod);
        }
    }

    void importSubRecord(LImport in, Mod srcMod) throws BadRecord, DataFormatException, BadParameter {
        String nextType = Record.getNextType(in);
        SubRecord record = get(nextType);
        if (record != null) {
            record.parseData(record.extractRecordData(in), srcMod);
        } else {
            throw new BadRecord(getTypes().get(0).toString() + " doesn't know what to do with a " + nextType + " record.");
        }
    }

    public void remove(String in) {
        if (map.containsKey(in)) {
            map.remove(in);
        }
    }

    public int length(ModExporter out) {
        int length = 0;
        for (SubRecord s : this) {
            length += s.getTotalLength(out);
        }
        return length;
    }

    public abstract ArrayList<String> getTypes();

    public abstract ArrayList<String> getTopLevelTypes();

    public ArrayList<FormID> allFormIDs() {
        ArrayList<FormID> out = new ArrayList<>();
        for (SubRecord s : this) {
            out.addAll(s.allFormIDs());
        }
        return out;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof SubRecords)) {
            return false;
        }
        final SubRecords other = (SubRecords) obj;
        Iterator<SubRecord> lhs = this.iterator();
        Iterator<SubRecord> rhs = other.iterator();
        while (lhs.hasNext() && rhs.hasNext()) {
            SubRecord lhsNext = lhs.next();
            SubRecord rhsNext = rhs.next();
            if (!"EDID".equals(lhsNext.getType()) && !lhsNext.equals(rhsNext)) {
                return false;
            }
        }
        return !lhs.hasNext() && !rhs.hasNext();
    }
}
